require "picasa"
require "thread"

class Imagery < Thor
  include Thor::Actions

  desc "upload [-c] [-t <number>] DIR", "Uploads all photos and videos from given directory (pass --continue to continue uploading)"
  method_option :continue, type: :boolean, default: false, aliases: "-c", desc: "continue aborted upload"
  method_option :threads, type: :numeric, default: 8, aliases: "-t", desc: "specify threads number"
  def upload(dir = File.basename(Dir.getwd))
    require_credentials

    inside(dir, verbose: true) do
      extensions = Picasa::File::KnownExtensions.join("|")
      entries    = Dir.entries(".").select { |e| e =~ /\.(#{extensions})$/i }.sort
      album_name = File.basename(dir)

      if options[:continue]
        # We're looking for existing album and photos already uploaded
        albums = client.album.list(fields: "entry(title,gphoto:id)")
        album  = albums.entries.find { |a| a.title == album_name }
        if album
          say "Album #{album_name} found - continuing upload"
        else
          say "Album #{album_name} not found in your collection - aborting" and exit
        end

        photo_titles = client.album.show(album.id, fields: "entry/title").entries.map &:title

        entries.reject! { |e| photo_titles.include?(File.basename(e, ".*")) }
      else
        say("Creating album: #{album_name}")
        album = create_album(album_name)
      end

      mutex = Mutex.new
      entries_size = entries.size
      number = 0
      options[:threads].times.map do
        Thread.new(entries) do |files|
          while file = mutex.synchronize { files.pop }
            mutex.synchronize { number += 1 }
            say("Uploading file #{file} to album #{album.title} - #{number}/#{entries_size}")
            create_photo(album, file_path: file, mime_type: guess_mime_type(file))
          end
        end
      end.each(&:join)
      say "Finished uploading #{number} files"
    end
  end

  desc "resize [-w <number>] [-h <number>] DIR", "Resizes photos to given dimensions if exceeded"
  method_option :width, type: :numeric, default: 2048, aliases: "-w", desc: "max width"
  method_option :height, type: :numeric, default: 2048, aliases: "-h", desc: "max height"
  def resize(dir = ".")
    # Requires imagemagick library
    require "RMagick"
    inside(dir, verbose: true) do
      entries = Dir.entries(".").select { |e| e =~ /\.(jpg|jpeg|png|gif|bmp)$/i }.sort
      processed = 0
      entries.each do |file|
        photo = Magick::Image.read(file).first
        processed += 1
        GC.start # problems with memory leaking by RMagick
        if photo.columns > options[:width] || photo.rows > options[:height]
          say "Resizing #{file} to fit #{options[:width]}x#{options[:height]} - #{processed}/#{entries.size}"

          photo.resize_to_fit!(options[:width], options[:height])
          photo.write(File.basename(file))
          # Release memory
          photo.destroy!
        else
          say "Skipping #{file} - #{processed}/#{entries.size}"
        end
      end
    end
  end

  no_tasks do
    def client
      @client ||= Picasa::Client.new(
        user_id: ENV["GOOGLE_USER_ID"],
        access_token: ENV["GOOGLE_ACCESS_TOKEN"]
      )
    end

    def require_credentials
      say "You must specify GOOGLE_USER_ID env variable" and exit if ENV["GOOGLE_USER_ID"].nil?
      if ENV["GOOGLE_ACCESS_TOKEN"].nil?
        say "You must specify GOOGLE_ACCESS_TOKEN env variable" and exit
      end
    end

    def create_album(title)
      client.album.create(title: title)
    end

    def create_photo(album, options = {})
      client.photo.create(album.id, options)
    end

    def guess_mime_type(file)
      %x{file --mime-type -b #{file}}.chomp
    rescue
    end
  end
end
